package main

import (
	"context"
	"fmt"
	"io"
	proto "jaegerDemo/pb"
	"net"

	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

type GreeterServer struct {
	proto.UnimplementedGreeterServer
}

func (g *GreeterServer) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloReply, error) {
	out := &proto.HelloReply{
		Message: "hello",
	}
	return out, nil
}

func (g *GreeterServer) SayHi(ctx context.Context, in *proto.HiRequest) (*proto.HiReply, error) {
	out := &proto.HiReply{
		Message: "hi," + in.Name,
	}
	return out, nil
}

func main() {
	address := "localhost:8089"
	lis, err := net.Listen("tcp", address)
	if err != nil {
		panic(err)
	}
	s := grpc.NewServer(grpc.UnaryInterceptor(serverInterceptor()))
	proto.RegisterGreeterServer(s, &GreeterServer{})
	fmt.Println("监听:", address)
	if err := s.Serve(lis); err != nil {
		panic(err)
	}
}

// 拦截器
func serverInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		defer recoverMid()
		span, close := jaegerInit(ctx, info.FullMethod)
		defer close.Close()
		defer span.Finish()
		res, err := handler(ctx, req)
		if err != nil {
			span.LogKV("grpc_err", err)
		}
		return res, err
	}
}

// 格式化jaeger
func jaegerInit(ctx context.Context, serviceName string) (opentracing.Span, io.Closer) {
	cfg := jaegercfg.Configuration{
		ServiceName: "grpc_service", // 对其发起请求的的调用链，叫什么服务
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:          true,
			CollectorEndpoint: "http://localhost:14268/api/traces",
		},
	}
	tracer, close, err := cfg.NewTracer()
	if err != nil {
		panic(err)
	}
	// 通过context读取出metadata
	md, _ := metadata.FromIncomingContext(ctx)
	// 设置为全局tracer,方便调用。go里面，每一个请求都有一个独立的协程去运行，所以这里设置为全局tracer不影响其他请求
	opentracing.SetGlobalTracer(tracer)
	// 将metadata解析出传递过来的span context
	spanCtx, err := tracer.Extract(
		opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(md),
	)
	if err != nil {
		panic(err)
	}
	// 创建子span
	span := tracer.StartSpan(serviceName, opentracing.ChildOf(spanCtx))
	return span, close
}

// 异常抓取中间件，防止panic导致程序停止运行
func recoverMid() {
	if err := recover(); err != nil {
		fmt.Printf("grpc service err:%v\n", err)
	}
}
